﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Xml;
using System.Xml.Linq;

using System.Windows;
using System.Windows.Forms;

using HtmlAgilityPack;

namespace Norbauer.Office.OneNote.Addins.Commonplacing
{

    class Tools
    {
        public static void standardizeFormattingOfCurrentPage()
        {
            removePageColorOfCurrentPage();
            standardizeSpacingOfCurrentPage(0, 0, 10);
            standardizeBaseFontOfCurrentPage("Calibri", 11);
        }

        public static void standardizeSpacingOfCurrentPage(float spaceBefore, float spaceBetween, float spaceAfter)
        {
            var preferredParagraphSpacingSettings = new Dictionary<string, float>() 
            { 
                {"spaceBefore", spaceBefore},
                {"spaceBetween", spaceBetween},
                {"spaceAfter", spaceAfter}
            };

            var oneNote = new OneNoteApplicationConnection();
            XElement pageRoot = oneNote.getCurrentPageAsXElement();

            // OE is the parent container where the spacing is set for child T (paragraph) elements
            // I'm using this long descendents chain just as a sort of sanity check, to make sure we're only modifying (OE) elements well down into the hierarchy with the appropriate parents
            foreach (XElement OEElement in pageRoot.Descendants(oneNote.oneNoteDocumentXMLNamespace + "Outline").Descendants(oneNote.oneNoteDocumentXMLNamespace + "OEChildren").Descendants(oneNote.oneNoteDocumentXMLNamespace + "OE"))
            {
                if (containsParagraphLevelContent(OEElement, pageRoot, oneNote))
                {
                    foreach (KeyValuePair<string, float> preferredSpacingSetting in preferredParagraphSpacingSettings)
                    {
                        float? originalSpacingSettingFromOneNotePage = null;
                        XAttribute spacingAttributeOfCurrentElementWithinInMemoryOneNotePage = OEElement.Attribute(preferredSpacingSetting.Key);

                        if (spacingAttributeOfCurrentElementWithinInMemoryOneNotePage != null)
                        {
                            originalSpacingSettingFromOneNotePage = (float)spacingAttributeOfCurrentElementWithinInMemoryOneNotePage;
                        }

                        if ((originalSpacingSettingFromOneNotePage == null && preferredSpacingSetting.Value != 0) ||
                             (originalSpacingSettingFromOneNotePage != preferredSpacingSetting.Value)
                           )
                        {
                            OEElement.SetAttributeValue(preferredSpacingSetting.Key, preferredSpacingSetting.Value);
                        }
                    }
                }

            }
            oneNote.writePageAsXElementBackToDataStore(pageRoot);
        }

        public static void standardizeBaseFontOfCurrentPage(string fontName, float fontSizeInPoints)
        {
            var oneNote = new OneNoteApplicationConnection();
            XElement pageRoot = oneNote.getCurrentPageAsXElement();

            foreach (XElement OEElement in pageRoot.Descendants(oneNote.oneNoteDocumentXMLNamespace + "Outline").Descendants(oneNote.oneNoteDocumentXMLNamespace + "OEChildren").Descendants(oneNote.oneNoteDocumentXMLNamespace + "OE"))
            {
                if (containsParagraphLevelContent(OEElement, pageRoot, oneNote))
                {
                    string newStyleString;

                    XAttribute style = OEElement.Attribute("style");
                    if (style == null)
                    {
                        newStyleString = String.Format("font-family:{0};font-size:{1}pt;", fontName, fontSizeInPoints);
                    }
                    else
                    {
                        // e.g. "font-family:Calibri;font-size:11.0pt;color:red"
                        string currentStyleString = style.Value;

                        var stylePairs = new Dictionary<string, string>();

                        // e.g., ["font-family:Calibri","font-size:11.0pt","color:red"]
                        foreach (string stylePair in currentStyleString.Split(';'))
                        {
                            if (stylePair != "") // because "x;x;" yields an array of length 3 with a trailing empty string
                            {   // e.g., ["font-family","Calibri"]
                                string[] partsOfStylePair = stylePair.Split(':');
                                stylePairs.Add(partsOfStylePair[0], partsOfStylePair[1]);
                            }
                        }

                        stylePairs["font-family"] = fontName;
                        stylePairs["font-size"] = String.Format("{0}pt", fontSizeInPoints);

                        newStyleString = stylePairs.Aggregate("",
                            (finalString, stylePair) =>
                                String.Format("{0}{1}:{2};", finalString, stylePair.Key, stylePair.Value)
                                );

                    }
                    OEElement.SetAttributeValue("style", newStyleString);

                    // remove any font settings on child Number or Bullet elements so they will inherit the new base font
                    var listElements = OEElement.Descendants(oneNote.oneNoteDocumentXMLNamespace + "Number").Concat(OEElement.Descendants(oneNote.oneNoteDocumentXMLNamespace + "Bullet"));
                    foreach (XElement listElement in listElements)
                    {
                        listElement.SetAttributeValue("fontColor", null);
                        listElement.SetAttributeValue("fontSize", null);
                    }
                }
            }
            oneNote.writePageAsXElementBackToDataStore(pageRoot);
        }

        public static void removePageColorOfCurrentPage()
        {
            var oneNote = new OneNoteApplicationConnection();
            XElement pageRoot = oneNote.getCurrentPageAsXElement();

            XElement pageSettings = pageRoot.Descendants(oneNote.oneNoteDocumentXMLNamespace + "PageSettings").FirstOrDefault();

            pageSettings.SetAttributeValue("color", "automatic");
            oneNote.writePageAsXElementBackToDataStore(pageRoot);
        }

        public static void addLinkToCurrentPageToCurrentClipboardText()
        {
            string clipboard_html;

            try
            {
                // We have to use the HtmlFragment library, because .Net adds a garbage header to the HTML on the clipboard.
                clipboard_html = HtmlFragment.FromClipboard().Context;
            }
            catch (FormatException)
            {
                return; // fail silently if no html on clipboard
            }

            // remove further problematic garbage added by .Net
            clipboard_html = clipboard_html.Replace("<!--EndFragment-->", "");

            var oneNote = new OneNoteApplicationConnection();

            // We have to use the HTML Agility Pack (HtmlDocument) rather than LINQ to XML because we're generally dealing with malformed HTML.
            var clipboardHtmlAsHtmlDocument = new HtmlAgilityPack.HtmlDocument();
            clipboardHtmlAsHtmlDocument.LoadHtml(clipboard_html);

            // to prevent multiple links form piling up on the clipboard if the method is called more than once
            var preExistingLinks = clipboardHtmlAsHtmlDocument.DocumentNode.Descendants("a");
            if (preExistingLinks.Count() != 0)
                return;

            HtmlNode newParagraph = clipboardHtmlAsHtmlDocument.CreateElement("p");

            HtmlNode newLink = clipboardHtmlAsHtmlDocument.CreateElement("a");
            newLink.Attributes.Add("href", oneNote.getURLForFirstSelectedElementInCurrentPage());
            newLink.InnerHtml = oneNote.getTitleOfCurrentPageAsHtmlFragmentString();

            newParagraph.ChildNodes.Append(newLink);

            clipboardHtmlAsHtmlDocument.DocumentNode.Descendants("body").FirstOrDefault().ChildNodes.Append(newParagraph);

            // this command will allow you to get the current document HTML as a string; otherwise it's the Save method to stream to a file.
            string modifiedHtml = clipboardHtmlAsHtmlDocument.DocumentNode.OuterHtml;
            HtmlFragment.CopyToClipboard(modifiedHtml);
        }

        public static void copyCleanedUpTextOfCurrentlySelectedOCRImageToClipboard()
        {
            var oneNote = new OneNoteApplicationConnection();
            XNamespace oneNoteDocumentXMLNamespace = oneNote.oneNoteDocumentXMLNamespace;
            XElement pageRoot = oneNote.getCurrentPageAsXElement();

            IEnumerable<XElement> selectedXElementsCollection =
                from image in pageRoot.Descendants(oneNoteDocumentXMLNamespace + "Image")
                where ((string)image.Attribute("selected") == "all")
                select image;

            XElement firstSelectedImage = selectedXElementsCollection.FirstOrDefault();
            if (firstSelectedImage == null)
                return;

            XElement OCRTextOfFirstSelectedImage = firstSelectedImage.Descendants(oneNoteDocumentXMLNamespace + "OCRText").FirstOrDefault();
            if (OCRTextOfFirstSelectedImage == null)
                return;

            string rawOCRText = OCRTextOfFirstSelectedImage.Value;

            StringBuilder cleanedText = new StringBuilder(rawOCRText);
            // remove any hyphens followed by newlines (sadly, this rarely happens in OneNote's shitty OCR)
            cleanedText = cleanedText.Replace("-\n", "");
            // remove newlines
            cleanedText = cleanedText.Replace("\n", " ");
            // remove curly quotes
            cleanedText = cleanedText.Replace('\u2018', '\'').Replace('\u2019', '\'').Replace('\u201c', '\"').Replace('\u201d', '\"');

            TextClipboard.CopyToClipboard(cleanedText.ToString());
        }

        private static bool containsParagraphLevelContent(XElement OEElement, XElement pageRoot, OneNoteApplicationConnection oneNote)
        {
            Dictionary<string, string> quickStyleDefsFromPage = XMLHelpers.getQuickStyleDefsFromPageXElement(pageRoot, oneNote.oneNoteDocumentXMLNamespace);

            bool quickStyleIndexAbsent = false;
            bool quickStyleIndexMapsToParagraph = false;

            if (OEElement.Attribute("quickStyleIndex") == null)
            {
                quickStyleIndexAbsent = true;
            }
            else
            {
                string quickStyleIndexValueOfCurrentOEElement = OEElement.Attribute("quickStyleIndex").Value;
                quickStyleIndexMapsToParagraph = quickStyleDefsFromPage[quickStyleIndexValueOfCurrentOEElement] == "p";
            }

            return (quickStyleIndexAbsent || quickStyleIndexMapsToParagraph);
        }

    }
}
